#!/usr/bin/python
# -*- coding: UTF-8 -*-
from __future__ import absolute_import
from __future__ import unicode_literals
import os
import cv2
import time
import base64
import traceback
import json
import numpy as np
# import matplotlib.pyplot as plt
# from sklearn.datasets import load_iris
import matplotlib.pyplot as plt
from PIL import Image, ImageTk
# import random
from tkinter import ttk, Tk, Canvas, TclError
import tkinter as tk
from algo_pack import image_binary, image_filter, image_edge, image_morph, image_transf, image_contrast
from tkinter import messagebox


class AutoScrollbar(ttk.Scrollbar):
    ''' A scrollbar that hides itself if it's not needed.
        Works only if you use the grid geometry manager '''

    def set(self, lo, hi):
        if float(lo) <= 0.0 and float(hi) >= 1.0:
            self.grid_remove()
        else:
            self.grid()
            ttk.Scrollbar.set(self, lo, hi)

    def pack(self, **kw):
        raise TclError('Cannot use pack with this widget')

    def place(self, **kw):
        raise TclError('Cannot use place with this widget')


class Zoom_Advanced(ttk.Frame):
    ''' Advanced zoom of the image '''

    def __init__(self, mainframe, image_cv_, win_title_str):
        ''' Initialize the main Frame '''
        # master代表主窗口
        ttk.Frame.__init__(self, master=mainframe)
        self.master.geometry("500x500")
        self.master.title(win_title_str)

        # Vertical and horizontal scrollbars for canvas
        vbar = AutoScrollbar(self.master, orient='vertical')
        hbar = AutoScrollbar(self.master, orient='horizontal')
        vbar.grid(row=0, column=1, sticky='ns')
        hbar.grid(row=1, column=0, sticky='we')

        # Create canvas and put image on it
        self.canvas = Canvas(self.master, highlightthickness=0,
                             xscrollcommand=hbar.set, yscrollcommand=vbar.set)
        self.canvas.grid(row=0, column=0, sticky='nswe')
        self.canvas.update()  # wait till canvas is created
        # Get component size
        self.vbar_w = vbar.winfo_width()
        self.vbar_h = vbar.winfo_height()
        self.hbar_w = hbar.winfo_width()
        self.hbar_h = hbar.winfo_height()
        self.canvas_w = self.canvas.winfo_width()
        self.canvas_h = self.canvas.winfo_height()

        vbar.configure(command=self.scroll_y)  # bind scrollbars to the canvas
        hbar.configure(command=self.scroll_x)
        # Make the canvas expandable
        self.master.rowconfigure(0, weight=1)
        self.master.columnconfigure(0, weight=1)
        # Bind events to the Canvas
        self.canvas.bind('<Configure>', self.show_image)  # canvas is resized
        self.canvas.bind('<ButtonPress-1>', self.move_from)
        self.canvas.bind('<B1-Motion>', self.move_to)
        self.canvas.bind('<MouseWheel>', self.wheel)  # with Windows and MacOS, but not Linux
        # self.canvas.bind('<Button-5>', self.wheel)  # only with Linux, wheel scroll down
        # self.canvas.bind('<Button-4>', self.wheel)  # only with Linux, wheel scroll up
        # self.image_pil = Image.open(path)  # open image
        self.image_cv = np.array(image_cv_)  # 接收到的输入图像
        self.image_current_cv = None         # 当前图像的一份备份
        self.image_binary_cv = None          # 二值化图像
        self.image_filter_cv = None          # 滤噪后的图像
        self.image_edge_cv = None            # 边缘提取后的图像
        self.image_morph_cv = None           # 形态学后的图像
        self.image_rotate_cv = None
        self.image_flip_cv = None
        self.image_resize_cv = None
        self.image_contrast_cv = None
        self.image_skeleton_cv = None
        self.image_blob_cv = None
        self.image_hough_cv = None
        self.image_proj_cv = None
        self.image_template_match_cv = None

        self.image_pil = Image.fromarray(image_cv_)  # 接收到的输入图像PIL格式
        self.width, self.height = self.image_pil.size
        self.imscale = 1.0  # scale for the canvaas image
        self.delta = 1.3  # zoom magnitude
        # Put image into container rectangle and use it to set proper coordinates to the image
        self.container = self.canvas.create_rectangle(0, 0, self.width, self.height, width=0)
        # self.show_image()

        # 获取下拉菜单字符串
        self.var_algo_op = tk.StringVar()
        self.var_xy_select = tk.StringVar()
        # 获取滚动条字符串
        self.scale_str = tk.IntVar()
        self.scale_sigma_str = tk.StringVar()
        # 滚动条对象
        self.scl_kernel = None
        self.scl_sigma = None
        # 下拉菜单对象
        self.comb_direction_op = None

        # 获取滚动条的字符串
        self.scl_low_str = tk.StringVar()
        self.scl_high_str = tk.StringVar()
        # 滚动条对象(Canny高低阈值)
        self.scl_low_th = None
        self.scl_high_th = None
        # 滚动条对象（形态学kernel行列）
        self.scl_1 = None
        self.scl_2 = None
        # 获取滚动条的字符串
        self.scl_str_1 = tk.StringVar()
        self.scl_str_2 = tk.StringVar()
        # 下拉菜单对象
        self.comb_1 = None
        # 获取下拉菜单字符串
        self.comb_str_1 = tk.StringVar()
        # 获取button对象
        self.button_1 = None
        self.button_2 = None
        self.button_3 = None
        # 获取旋转标志位
        self.rotate_flag = 0
        # 获取翻转标志位
        self.flip_flag = 0
        # 获取骨架算法切换标志位
        self.skeleton_flag = 1
        self.is_close_win = False
        # 获取文本框
        self.entry_1 = None
        self.entry_2 = None
        self.interpolation_method = cv2.INTER_LINEAR

    def scroll_y(self, *args, **kwargs):
        ''' Scroll canvas vertically and redraw the image '''
        self.canvas.yview(*args, **kwargs)  # scroll vertically
        self.show_image()  # redraw the image

    def scroll_x(self, *args, **kwargs):
        ''' Scroll canvas horizontally and redraw the image '''
        self.canvas.xview(*args, **kwargs)  # scroll horizontally
        self.show_image()  # redraw the image

    def move_from(self, event):
        ''' Remember previous coordinates for scrolling with the mouse '''
        self.canvas.scan_mark(event.x, event.y)

    def move_to(self, event):
        ''' Drag (move) canvas to the new position '''
        self.canvas.scan_dragto(event.x, event.y, gain=1)
        self.show_image()  # redraw the image

    def wheel(self, event):
        ''' Zoom with mouse wheel '''
        x = self.canvas.canvasx(event.x)
        y = self.canvas.canvasy(event.y)
        scale = 1.0

        # Respond to Linux (event.num) or Windows (event.delta) wheel event
        if event.num == 5 or event.delta == -120:  # scroll down
            i = min(self.width, self.height)
            if int(i * self.imscale) < 30: return  # image is less than 30 pixels

            # self.imscale = self.scale_fix[self.scale_pos]
            # self.scale_pos = self.scale_pos - 1
            # if self.scale_pos < 0:
            #     self.scale_pos = 0

            self.imscale /= self.delta
            scale /= self.delta
        if event.num == 4 or event.delta == 120:  # scroll up
            i = min(self.canvas.winfo_width(), self.canvas.winfo_height())
            if i < self.imscale: return  # 1 pixel is bigger than the visible area

            # self.imscale = self.scale_fix[self.scale_pos]
            # self.scale_pos = self.scale_pos + 1
            # if self.scale_pos >= self.scale_len:
            #     self.scale_pos = len(self.scale_fix)-1

            self.imscale *= self.delta
            scale *= self.delta

        self.canvas.scale('all', x, y, scale, scale)  # rescale all canvas objects
        self.show_image()

    def update_bw(self, event):
        current_th = int(self.scale_str.get())
        self.image_binary_cv = image_binary.manual_threshold(self.image_cv, current_th)
        self.image_current_cv = np.array(self.image_binary_cv)
        self.image_pil = Image.fromarray(self.image_binary_cv)
        self.show_image()

    def get_binary_comb_str(self, event):
        current_text = self.var_algo_op.get()
        if current_text == '恢复':
            if self.scl_kernel is not None:
                # self.scl_kernel.place_forget()  # 首先删除手动阈值的滚动条
                self.scl_kernel.destroy()
            self.image_binary_cv = np.array(self.image_cv)
            self.image_current_cv = np.array(self.image_cv)
            self.image_pil = Image.fromarray(self.image_cv)
            self.show_image()

        elif current_text == 'Manual thresh':
            # 当选定算法时，首先初始化一次算法运行结果，当激活了某个参数事件，再次运行和显示算法+参数的结果
            self.image_binary_cv = image_binary.manual_threshold(self.image_cv, 127)
            self.image_current_cv = np.array(self.image_binary_cv)
            self.image_pil = Image.fromarray(self.image_binary_cv)
            self.show_image()

            # 阈值滚动条
            self.scl_kernel = tk.Scale(self.master, from_=0, to=255, length=150, orient=tk.HORIZONTAL,
                                variable=self.scale_str, command=self.update_bw)
            self.scl_kernel.set(127)
            self.scl_kernel.place(x=200, y=0)

        elif current_text == 'Adaptive thresh':
            if self.scl_kernel is not None:
                # self.scl_kernel.place_forget()  # 首先删除手动阈值的滚动条
                self.scl_kernel.destroy()
            get_th_type_list = [cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.ADAPTIVE_THRESH_MEAN_C]
            get_bw_type_list = [cv2.THRESH_BINARY]
            self.image_binary_cv = image_binary.adaptive_threshold(self.image_cv, 255, get_th_type_list[0], get_bw_type_list[0])
            self.image_current_cv = np.array(self.image_binary_cv)
            self.image_pil = Image.fromarray(self.image_binary_cv)
            self.show_image()

        elif current_text == 'OTSU':
            if self.scl_kernel is not None:
                # self.scl_kernel.place_forget()  # 首先删除手动阈值的滚动条
                self.scl_kernel.destroy()
            self.image_binary_cv = image_binary.otsu(self.image_cv)
            self.image_current_cv = np.array(self.image_binary_cv)
            self.image_pil = Image.fromarray(self.image_binary_cv)
            self.show_image()

        elif current_text == 'SAUVOLA':
            if self.scl_kernel is not None:
                # self.scl_kernel.place_forget()  # 首先删除手动阈值的滚动条
                self.scl_kernel.destroy()
            self.image_binary_cv = image_binary.sauvola(self.image_cv)
            self.image_current_cv = np.array(self.image_binary_cv)
            self.image_pil = Image.fromarray(self.image_binary_cv)
            self.show_image()

        elif current_text == 'NIBLACK':
            if self.scl_kernel is not None:
                # self.scl_kernel.place_forget()  # 首先删除手动阈值的滚动条
                self.scl_kernel.destroy()
            self.image_binary_cv = image_binary.niblack(self.image_cv)
            self.image_current_cv = np.array(self.image_binary_cv)
            self.image_pil = Image.fromarray(self.image_binary_cv)
            self.show_image()
        print(current_text)

    def binary_operate(self):
        # 1） 添加下拉菜单，选择二值化算法
        comb = tk.ttk.Combobox(self.master, textvariable=self.var_algo_op)
        comb['value'] = ('--- Select binary algo --- ',
                         '恢复',
                         'Manual thresh',
                         'Adaptive thresh',
                         'OTSU',
                         'SAUVOLA',
                         'NIBLACK')
        # 这步初始化操作目的：弹出binary窗口的时候，如果不做任何操作，image_binary_cv 是None，
        # 主窗口的image_current都是没有图像的，会报错
        self.image_binary_cv = np.array(self.image_cv)
        comb.current(0)
        comb.bind('<<ComboboxSelected>>', self.get_binary_comb_str)
        comb.place(x=0, rely=0)

    def update_linear_enhance(self, event):
        alpha = float(self.scl_str_1.get())
        beta = int(self.scl_str_2.get())
        self.image_contrast_cv = image_contrast.linear_enhance(self.image_cv, alpha, beta)
        self.image_current_cv = np.array(self.image_contrast_cv)
        self.image_pil = Image.fromarray(self.image_contrast_cv)
        self.show_image()

    def update_hist_norm_enhance(self, event):
        alpha = float(self.scl_str_1.get())
        beta = int(self.scl_str_2.get())
        self.image_contrast_cv = image_contrast.hist_norm_enhance(self.image_cv, alpha, beta)
        self.image_current_cv = np.array(self.image_contrast_cv)
        self.image_pil = Image.fromarray(self.image_contrast_cv)
        self.show_image()

    def update_gamma_enhance(self, event):
        gamma = float(self.scl_str_1.get())
        self.image_contrast_cv = image_contrast.gamma_enhance(self.image_cv, gamma)
        self.image_current_cv = np.array(self.image_contrast_cv)
        self.image_pil = Image.fromarray(self.image_contrast_cv)
        self.show_image()

    def update_clahe_enhance(self, event):
        th_limit = float(self.scl_str_1.get())
        grid_size = int(self.scl_str_2.get())
        self.image_contrast_cv = image_contrast.clahe_enhance(self.image_cv, th_limit, grid_size)
        self.image_current_cv = np.array(self.image_contrast_cv)
        self.image_pil = Image.fromarray(self.image_contrast_cv)
        self.show_image()

    def get_contrast_comb_str(self, event):
        current_text = self.var_algo_op.get()
        if current_text == '恢复':
            if self.scl_1 is not None:
                self.scl_1.destroy()
            if self.scl_2 is not None:
                self.scl_2.destroy()
            self.image_contrast_cv = np.array(self.image_cv)
            self.image_current_cv = np.array(self.image_cv)
            self.image_pil = Image.fromarray(self.image_cv)
            self.show_image()

        elif current_text == 'Linear enhance':
            if self.scl_1 is not None:
                self.scl_1.destroy()
            if self.scl_2 is not None:
                self.scl_2.destroy()

            # 当选定算法时，首先初始化一次算法运行结果，当激活了某个参数事件，再次运行和显示算法+参数的结果
            self.image_contrast_cv = image_contrast.linear_enhance(self.image_cv)
            self.image_current_cv = np.array(self.image_contrast_cv)
            self.image_pil = Image.fromarray(self.image_contrast_cv)
            self.show_image()
            # 阈值滚动条1:  alpha
            self.scl_1 = tk.Scale(self.master, from_=1, resolution=0.5, to=25, length=150, orient=tk.HORIZONTAL,
                                label='alpha', variable=self.scl_str_1, command=self.update_linear_enhance)
            self.scl_1.set(1)
            self.scl_1.place(x=170, y=0)
            # 阈值滚动条2: beta
            self.scl_2 = tk.Scale(self.master, from_=0, resolution=1, to=50, length=150, orient=tk.HORIZONTAL,
                                      label='beta', variable=self.scl_str_2, command=self.update_linear_enhance)
            self.scl_2.set(0)
            self.scl_2.place(x=330, y=0)

        elif current_text == 'Hist norm enhance':
            if self.scl_1 is not None:
                self.scl_1.destroy()
            if self.scl_2 is not None:
                self.scl_2.destroy()
            # 当选定算法时，首先初始化一次算法运行结果，当激活了某个参数事件，再次运行和显示算法+参数的结果
            self.image_contrast_cv = image_contrast.hist_norm_enhance(self.image_cv)
            self.image_current_cv = np.array(self.image_contrast_cv)
            self.image_pil = Image.fromarray(self.image_contrast_cv)
            self.show_image()
            # 阈值滚动条1:  alpha
            self.scl_1 = tk.Scale(self.master, from_=1, resolution=1, to=400, length=150, orient=tk.HORIZONTAL,
                                  label='alpha', variable=self.scl_str_1, command=self.update_hist_norm_enhance)
            self.scl_1.set(350)
            self.scl_1.place(x=170, y=0)
            # 阈值滚动条2: beta
            self.scl_2 = tk.Scale(self.master, from_=1, resolution=1, to=400, length=150, orient=tk.HORIZONTAL,
                                  label='beta', variable=self.scl_str_2, command=self.update_hist_norm_enhance)
            self.scl_2.set(10)
            self.scl_2.place(x=330, y=0)

        elif current_text == 'Gamma enhance':
            if self.scl_1 is not None:
                self.scl_1.destroy()
            if self.scl_2 is not None:
                self.scl_2.destroy()
            # 当选定算法时，首先初始化一次算法运行结果，当激活了某个参数事件，再次运行和显示算法+参数的结果
            self.image_contrast_cv = image_contrast.gamma_enhance(self.image_cv)
            self.image_current_cv = np.array(self.image_contrast_cv)
            self.image_pil = Image.fromarray(self.image_contrast_cv)
            self.show_image()
            # 阈值滚动条1:  gamma
            self.scl_1 = tk.Scale(self.master, from_=0, resolution=0.1, to=10, length=150, orient=tk.HORIZONTAL,
                                  label='gamma', variable=self.scl_str_1, command=self.update_gamma_enhance)
            self.scl_1.set(0.4)
            self.scl_1.place(x=170, y=0)

        elif current_text == 'Hist equa enhance':
            if self.scl_1 is not None:
                self.scl_1.destroy()
            if self.scl_2 is not None:
                self.scl_2.destroy()
            # 当选定算法时，首先初始化一次算法运行结果，当激活了某个参数事件，再次运行和显示算法+参数的结果
            self.image_contrast_cv = image_contrast.hist_equa_enhance(self.image_cv)
            self.image_current_cv = np.array(self.image_contrast_cv)
            self.image_pil = Image.fromarray(self.image_contrast_cv)
            self.show_image()

        elif current_text == 'Clahe enhance':
            if self.scl_1 is not None:
                self.scl_1.destroy()
            if self.scl_2 is not None:
                self.scl_2.destroy()
            # 当选定算法时，首先初始化一次算法运行结果，当激活了某个参数事件，再次运行和显示算法+参数的结果
            self.image_contrast_cv = image_contrast.hist_norm_enhance(self.image_cv)
            self.image_current_cv = np.array(self.image_contrast_cv)
            self.image_pil = Image.fromarray(self.image_contrast_cv)
            self.show_image()
            # 阈值滚动条1: th_limit_: 限制对比度的阈值，默认为40，直方图中像素值出现次数大于该阈值，多余的次数会被重新分配
            self.scl_1 = tk.Scale(self.master, from_=1, resolution=1, to=100, length=150, orient=tk.HORIZONTAL,
                                  label='thresh lim', variable=self.scl_str_1, command=self.update_clahe_enhance)
            self.scl_1.set(10)
            self.scl_1.place(x=170, y=0)
            # 阈值滚动条2: grid_size_: 图像会被划分的size， 如tileGridSize=(8,8),默认为(8,8)
            self.scl_2 = tk.Scale(self.master, from_=1, resolution=1, to=30, length=150, orient=tk.HORIZONTAL,
                                  label='grid size', variable=self.scl_str_2, command=self.update_clahe_enhance)
            self.scl_2.set(8)
            self.scl_2.place(x=330, y=0)

    def contrast_operate(self):
        # 1） 添加下拉菜单，选择二值化算法
        comb = tk.ttk.Combobox(self.master, textvariable=self.var_algo_op)
        comb['value'] = ('--- Select contrast algo --- ',
                         '恢复',
                         'Linear enhance',
                         'Hist norm enhance',
                         'Gamma enhance',
                         'Hist equa enhance',
                         'Clahe enhance')
        # 这步初始化操作目的：弹出binary窗口的时候，如果不做任何操作，image_binary_cv 是None，
        # 主窗口的image_current都是没有图像的，会报错
        self.image_contrast_cv = np.array(self.image_cv)
        comb.current(0)
        comb.bind('<<ComboboxSelected>>', self.get_contrast_comb_str)
        comb.place(x=0, rely=0)

    def update_gaussian_filter(self, event):
        kernel_size = int(self.scale_str.get()) + 1
        sigma = float(self.scale_sigma_str.get())
        self.image_filter_cv = image_filter.gaussian_filter(self.image_cv, kernel_size, sigma)
        self.image_current_cv = np.array(self.image_filter_cv)
        self.image_pil = Image.fromarray(self.image_filter_cv)
        self.show_image()

    def update_mean_filter(self, event):
        kernel_size = int(self.scale_str.get()) + 1
        self.image_filter_cv = image_filter.mean_filter(self.image_cv, kernel_size)
        self.image_current_cv = np.array(self.image_filter_cv)
        self.image_pil = Image.fromarray(self.image_filter_cv)
        self.show_image()

    def update_median_filter(self, event):
        kernel_size = int(self.scale_str.get()) + 1
        self.image_filter_cv = image_filter.median_filter(self.image_cv, kernel_size)
        self.image_current_cv = np.array(self.image_filter_cv)
        self.image_pil = Image.fromarray(self.image_filter_cv)
        self.show_image()

    def update_bilateral_filter(self, event):
        kernel_size = int(self.scale_str.get()) + 1
        sigma = float(self.scale_sigma_str.get())

        self.image_filter_cv = image_filter.bilateral_filter(self.image_cv, kernel_size, sigma)
        self.image_current_cv = np.array(self.image_filter_cv)
        self.image_pil = Image.fromarray(self.image_filter_cv)
        self.show_image()

    def get_filter_comb_str(self, event):
        current_text = self.var_algo_op.get()
        if current_text == '恢复':
            if self.scl_kernel is not None:
                self.scl_kernel.destroy()
            if self.scl_sigma is not None:
                self.scl_sigma.destroy()
            self.image_filter_cv = np.array(self.image_cv)
            self.image_current_cv = np.array(self.image_cv)
            self.image_pil = Image.fromarray(self.image_cv)
            self.show_image()

        elif current_text == 'Gaussian filter':
            if self.scl_kernel is not None:
                self.scl_kernel.destroy()
            if self.scl_sigma is not None:
                self.scl_sigma.destroy()

            # 当选定算法时，首先初始化一次算法运行结果，当激活了某个参数事件，再次运行和显示算法+参数的结果
            self.image_filter_cv = image_filter.gaussian_filter(self.image_cv)
            self.image_current_cv = np.array(self.image_filter_cv)
            self.image_pil = Image.fromarray(self.image_filter_cv)
            self.show_image()

            # 阈值滚动条1:  kernel_size
            self.scl_kernel = tk.Scale(self.master, from_=2, resolution=2, to=50, length=150, orient=tk.HORIZONTAL,
                                label='kernel size+1', variable=self.scale_str, command=self.update_gaussian_filter)
            self.scl_kernel.set(2)
            self.scl_kernel.place(x=170, y=0)

            # 阈值滚动条2: sigma
            self.scl_sigma = tk.Scale(self.master, from_=0.0, resolution=0.5, to=100, length=150, orient=tk.HORIZONTAL,
                                label='sigma', variable=self.scale_sigma_str, command=self.update_gaussian_filter)
            self.scl_sigma.set(0.0)
            self.scl_sigma.place(x=330, y=0)

        elif current_text == 'Bilateral filter':
            if self.scl_kernel is not None:
                self.scl_kernel.destroy()
            if self.scl_sigma is not None:
                self.scl_sigma.destroy()
            # 当选定算法时，首先初始化一次算法运行结果，当激活了某个参数事件，再次运行和显示算法+参数的结果
            self.image_filter_cv = image_filter.bilateral_filter(self.image_cv)
            self.image_current_cv = np.array(self.image_filter_cv)
            self.image_pil = Image.fromarray(self.image_filter_cv)
            self.show_image()

            # 阈值滚动条1:  kernel_size
            self.scl_kernel = tk.Scale(self.master, from_=2, resolution=2, to=50, length=150, orient=tk.HORIZONTAL,
                                label='kernel size+1', variable=self.scale_str, command=self.update_bilateral_filter)
            self.scl_kernel.set(2)
            self.scl_kernel.place(x=170, y=0)

            # 阈值滚动条2: sigma
            self.scl_sigma = tk.Scale(self.master, from_=1, resolution=1, to=100, length=150, orient=tk.HORIZONTAL,
                                      label='sigma', variable=self.scale_sigma_str, command=self.update_bilateral_filter)
            self.scl_sigma.set(1)
            self.scl_sigma.place(x=330, y=0)

        elif current_text == 'Mean filter':
            if self.scl_kernel is not None:
                # self.scl_kernel.place_forget()  # 首先删除手动阈值的滚动条1
                self.scl_kernel.destroy()
            if self.scl_sigma is not None:
                # self.scl_sigma.place_forget()  # 首先删除手动阈值的滚动条2
                self.scl_sigma.destroy()
            # 当选定算法时，首先初始化一次算法运行结果，当激活了某个参数事件，再次运行和显示算法+参数的结果
            self.image_filter_cv = image_filter.mean_filter(self.image_cv)
            self.image_current_cv = np.array(self.image_filter_cv)
            self.image_pil = Image.fromarray(self.image_filter_cv)
            self.show_image()

            # 阈值滚动条1:  kernel_size
            self.scl_kernel = tk.Scale(self.master, from_=2, resolution=2, to=50, length=150, orient=tk.HORIZONTAL,
                                label='kernel size+1', variable=self.scale_str, command=self.update_mean_filter)
            self.scl_kernel.set(2)
            self.scl_kernel.place(x=170, y=0)

        elif current_text == 'Median filter':
            if self.scl_kernel is not None:
                self.scl_kernel.destroy()
            if self.scl_sigma is not None:
                self.scl_sigma.destroy()
            # 当选定算法时，首先初始化一次算法运行结果，当激活了某个参数事件，再次运行和显示算法+参数的结果
            self.image_filter_cv = image_filter.median_filter(self.image_cv)
            self.image_current_cv = np.array(self.image_filter_cv)
            self.image_pil = Image.fromarray(self.image_filter_cv)
            self.show_image()

            # 阈值滚动条1:  kernel_size
            self.scl_kernel = tk.Scale(self.master, from_=2, resolution=2, to=50, length=150, orient=tk.HORIZONTAL,
                                label='kernel size+1', variable=self.scale_str, command=self.update_median_filter)
            self.scl_kernel.set(2)
            self.scl_kernel.place(x=170, y=0)

    def filter_operate(self):
        # 1） 添加下拉菜单，选择滤波算法
        comb = tk.ttk.Combobox(self.master, textvariable=self.var_algo_op)
        comb['value'] = ('--- Select filter noise algo --- ',
                         '恢复',
                         'Gaussian filter',
                         'Mean filter',
                         'Median filter',
                         'Bilateral filter')
        # 这步初始化操作目的：弹出filter窗口的时候，如果不做任何操作，image_filter_cv 是None，
        # 主窗口的image_current都是没有图像的，会报错
        self.image_filter_cv = np.array(self.image_cv)
        comb.current(0)
        comb.bind('<<ComboboxSelected>>', self.get_filter_comb_str)
        comb.place(x=0, rely=0)

    def update_laplacian_edge(self, event):
        kernel_size = int(self.scale_str.get()) + 1
        self.image_edge_cv = image_edge.laplacian_edge(self.image_cv, kernel_size)
        self.image_current_cv = np.array(self.image_edge_cv)
        self.image_pil = Image.fromarray(self.image_edge_cv)
        self.show_image()

    def update_sobel_edge(self, event):
        kernel_size = int(self.scale_str.get()) + 1
        direction_type = self.var_xy_select.get()
        self.image_edge_cv = image_edge.sobel_edge(self.image_cv, kernel_size, direction_type)
        self.image_current_cv = np.array(self.image_edge_cv)
        self.image_pil = Image.fromarray(self.image_edge_cv)
        self.show_image()

    def update_prewitt_edge(self, event):
        direction_type = self.var_xy_select.get()
        self.image_edge_cv = image_edge.prewitt_edge(self.image_cv, direction_type)
        self.image_current_cv = np.array(self.image_edge_cv)
        self.image_pil = Image.fromarray(self.image_edge_cv)
        self.show_image()

    def update_roberts_edge(self, event):
        direction_type = self.var_xy_select.get()
        self.image_edge_cv = image_edge.roberts_edge(self.image_cv, direction_type)
        self.image_current_cv = np.array(self.image_edge_cv)
        self.image_pil = Image.fromarray(self.image_edge_cv)
        self.show_image()

    def update_canny_edge(self, event):
        low_th = int(self.scl_low_str.get())
        high_th = int(self.scl_high_str.get())
        self.image_edge_cv = image_edge.canny_edge(self.image_cv, low_th, high_th)
        self.image_current_cv = np.array(self.image_edge_cv)
        self.image_pil = Image.fromarray(self.image_edge_cv)
        self.show_image()

    def get_edge_comb_str(self, event):
        current_text = self.var_algo_op.get()
        if current_text == '恢复':
            if self.scl_kernel is not None:
                self.scl_kernel.destroy()
            if self.comb_direction_op is not None:
                self.comb_direction_op.destroy()
            if self.scl_low_th is not None:
                self.scl_low_th.destroy()
            if self.scl_high_th is not None:
                self.scl_high_th.destroy()
            self.image_edge_cv = np.array(self.image_cv)
            self.image_current_cv = np.array(self.image_cv)
            self.image_pil = Image.fromarray(self.image_cv)
            self.show_image()

        elif current_text == 'Laplacian':
            if self.scl_kernel is not None:
                self.scl_kernel.destroy()
            if self.comb_direction_op is not None:
                self.comb_direction_op.destroy()
            if self.scl_low_th is not None:
                self.scl_low_th.destroy()
            if self.scl_high_th is not None:
                self.scl_high_th.destroy()
            # 当选定算法时，首先初始化一次算法运行结果，当激活了某个参数事件，再次运行和显示算法+参数的结果
            self.image_edge_cv = image_edge.laplacian_edge(self.image_cv, 3)
            self.image_current_cv = np.array(self.image_edge_cv)
            self.image_pil = Image.fromarray(self.image_edge_cv)
            self.show_image()
            # 滚动条触发事件
            self.scl_kernel = tk.Scale(self.master, from_=2, resolution=2, to=6, length=100, orient=tk.HORIZONTAL,
                                label='kernel size+1', variable=self.scale_str, command=self.update_laplacian_edge)
            self.scl_kernel.set(2)
            self.scl_kernel.place(x=170, y=0)

        elif current_text == 'Sobel':
            if self.scl_kernel is not None:
                self.scl_kernel.destroy()
            if self.comb_direction_op is not None:
                self.comb_direction_op.destroy()
            if self.scl_low_th is not None:
                self.scl_low_th.destroy()
            if self.scl_high_th is not None:
                self.scl_high_th.destroy()
            # 当选定算法时，首先初始化一次算法运行结果，当激活了某个参数事件，再次运行和显示算法+参数的结果
            self.image_edge_cv = image_edge.sobel_edge(self.image_cv, 3, 'X direction')
            self.image_current_cv = np.array(self.image_edge_cv)
            self.image_pil = Image.fromarray(self.image_edge_cv)
            self.show_image()
            # 滚动条触发事件
            self.scl_kernel = tk.Scale(self.master, from_=2, resolution=2, to=6, length=100, orient=tk.HORIZONTAL,
                                       label='kernel_size+1', variable=self.scale_str, command=self.update_sobel_edge)
            self.scl_kernel.set(2)
            self.scl_kernel.place(x=170, y=0)
            # 下拉菜单触发事件
            self.comb_direction_op = tk.ttk.Combobox(self.master, textvariable=self.var_xy_select, width=12)
            self.comb_direction_op['value'] = ('X direction', 'Y direction', 'X and Y direction')
            self.comb_direction_op.current(0)
            self.comb_direction_op.bind('<<ComboboxSelected>>', self.update_sobel_edge)
            self.comb_direction_op.place(x=280, y=0)

        elif current_text == 'Prewitt':
            if self.scl_kernel is not None:
                self.scl_kernel.destroy()
            if self.comb_direction_op is not None:
                self.comb_direction_op.destroy()
            if self.scl_low_th is not None:
                self.scl_low_th.destroy()
            if self.scl_high_th is not None:
                self.scl_high_th.destroy()
            # 当选定算法时，首先初始化一次算法运行结果，当激活了某个参数事件，再次运行和显示算法+参数的结果
            self.image_edge_cv = image_edge.prewitt_edge(self.image_cv, 'X direction')
            self.image_current_cv = np.array(self.image_edge_cv)
            self.image_pil = Image.fromarray(self.image_edge_cv)
            self.show_image()

            # 下拉菜单触发事件
            self.comb_direction_op = tk.ttk.Combobox(self.master, textvariable=self.var_xy_select, width=12)
            self.comb_direction_op['value'] = ('X direction', 'Y direction', 'X and Y direction')
            self.comb_direction_op.current(0)
            self.comb_direction_op.bind('<<ComboboxSelected>>', self.update_prewitt_edge)
            self.comb_direction_op.place(x=170, y=0)

        elif current_text == 'Roberts':
            if self.scl_kernel is not None:
                self.scl_kernel.destroy()
            if self.comb_direction_op is not None:
                self.comb_direction_op.destroy()
            if self.scl_low_th is not None:
                self.scl_low_th.destroy()
            if self.scl_high_th is not None:
                self.scl_high_th.destroy()
            # 当选定算法时，首先初始化一次算法运行结果，当激活了某个参数事件，再次运行和显示算法+参数的结果
            self.image_edge_cv = image_edge.roberts_edge(self.image_cv, 'X direction')
            self.image_current_cv = np.array(self.image_edge_cv)
            self.image_pil = Image.fromarray(self.image_edge_cv)
            self.show_image()

            # 下拉菜单触发事件
            self.comb_direction_op = tk.ttk.Combobox(self.master, textvariable=self.var_xy_select, width=12)
            self.comb_direction_op['value'] = ('X direction', 'Y direction', 'X and Y direction')
            self.comb_direction_op.current(0)
            self.comb_direction_op.bind('<<ComboboxSelected>>', self.update_roberts_edge)
            self.comb_direction_op.place(x=170, y=0)

        elif current_text == 'Canny':
            if self.scl_kernel is not None:
                self.scl_kernel.destroy()
            if self.comb_direction_op is not None:
                self.comb_direction_op.destroy()
            if self.scl_low_th is not None:
                self.scl_low_th.destroy()
            if self.scl_high_th is not None:
                self.scl_high_th.destroy()

            # 当选定算法时，首先初始化一次算法运行结果，当激活了某个参数事件，再次运行和显示算法+参数的结果
            self.image_edge_cv = image_edge.canny_edge(self.image_cv)
            self.image_current_cv = np.array(self.image_edge_cv)
            self.image_pil = Image.fromarray(self.image_edge_cv)
            self.show_image()
            # 滚动条触发事件
            self.scl_low_th = tk.Scale(self.master, from_=0, resolution=1, to=100, length=100, orient=tk.HORIZONTAL,
                                       label='low_th', variable=self.scl_low_str,
                                       command=self.update_canny_edge)
            self.scl_low_th.set(30)
            self.scl_low_th.place(x=170, y=0)

            self.scl_high_th = tk.Scale(self.master, from_=0, resolution=1, to=100, length=100, orient=tk.HORIZONTAL,
                                       label='high_th', variable=self.scl_high_str,
                                       command=self.update_canny_edge)
            self.scl_high_th.set(90)
            self.scl_high_th.place(x=280, y=0)

        elif current_text == 'LoG':
            if self.scl_kernel is not None:
                self.scl_kernel.destroy()
            if self.comb_direction_op is not None:
                self.comb_direction_op.destroy()
            if self.scl_low_th is not None:
                self.scl_low_th.destroy()
            if self.scl_high_th is not None:
                self.scl_high_th.destroy()

            # 当选定算法时，首先初始化一次算法运行结果，当激活了某个参数事件，再次运行和显示算法+参数的结果
            self.image_edge_cv = image_edge.log_edge(self.image_cv)
            self.image_current_cv = np.array(self.image_edge_cv)
            self.image_pil = Image.fromarray(self.image_edge_cv)
            self.show_image()

    def edge_operate(self):
        # 1） 添加下拉菜单，选择边缘提取算法
        comb = tk.ttk.Combobox(self.master, textvariable=self.var_algo_op)
        comb['value'] = ('--- Select edge detect algo --- ',
                         '恢复',
                         'Laplacian',
                         'Sobel',
                         'Prewitt',
                         'Roberts',
                         'Canny',
                         'LoG')
        # 这步初始化操作目的：弹出filter窗口的时候，如果不做任何操作，image_filter_cv 是None，
        # 主窗口的image_current都是没有图像的，会报错
        self.image_edge_cv = np.array(self.image_cv)
        comb.current(0)
        comb.bind('<<ComboboxSelected>>', self.get_edge_comb_str)
        comb.place(x=0, rely=0)

    def update_erode_morph(self, event):
        kernel_w = int(self.scl_str_1.get())
        kernel_h = int(self.scl_str_2.get())
        kernel_shape = self.comb_str_1.get()
        self.image_morph_cv = image_morph.erode_op(self.image_cv, kernel_h, kernel_w, kernel_shape)
        self.image_current_cv = np.array(self.image_morph_cv)
        self.image_pil = Image.fromarray(self.image_morph_cv)
        self.show_image()

    def update_dilate_morph(self, event):
        kernel_w = int(self.scl_str_1.get())
        kernel_h = int(self.scl_str_2.get())
        kernel_shape = self.comb_str_1.get()
        self.image_morph_cv = image_morph.dilate_op(self.image_cv, kernel_h, kernel_w, kernel_shape)
        self.image_current_cv = np.array(self.image_morph_cv)
        self.image_pil = Image.fromarray(self.image_morph_cv)
        self.show_image()

    def update_open_morph(self, event):
        kernel_w = int(self.scl_str_1.get())
        kernel_h = int(self.scl_str_2.get())
        kernel_shape = self.comb_str_1.get()
        self.image_morph_cv = image_morph.open_op(self.image_cv, kernel_h, kernel_w, kernel_shape)
        self.image_current_cv = np.array(self.image_morph_cv)
        self.image_pil = Image.fromarray(self.image_morph_cv)
        self.show_image()

    def update_close_morph(self, event):
        kernel_w = int(self.scl_str_1.get())
        kernel_h = int(self.scl_str_2.get())
        kernel_shape = self.comb_str_1.get()
        self.image_morph_cv = image_morph.close_op(self.image_cv, kernel_h, kernel_w, kernel_shape)
        self.image_current_cv = np.array(self.image_morph_cv)
        self.image_pil = Image.fromarray(self.image_morph_cv)
        self.show_image()

    def get_morph_comb_str(self, event):
        current_text = self.var_algo_op.get()
        if current_text == "恢复":
            if self.scl_1 is not None:
                self.scl_1.destroy()
            if self.scl_2 is not None:
                self.scl_2.destroy()
            if self.comb_1 is not None:
                self.comb_1.destroy()
            self.image_morph_cv = np.array(self.image_cv)
            self.image_current_cv = np.array(self.image_cv)
            self.image_pil = Image.fromarray(self.image_cv)
            self.show_image()

        elif current_text == "Erode":
            if self.scl_1 is not None:
                self.scl_1.destroy()
            if self.scl_2 is not None:
                self.scl_2.destroy()
            if self.comb_1 is not None:
                self.comb_1.destroy()
            # 当选定算法时，首先初始化一次算法运行结果，当激活了某个参数事件，再次运行和显示算法+参数的结果
            self.image_morph_cv = image_morph.erode_op(self.image_cv)
            self.image_current_cv = np.array(self.image_morph_cv)
            self.image_pil = Image.fromarray(self.image_morph_cv)
            self.show_image()
            # 下拉菜单触发事件
            self.comb_1 = tk.ttk.Combobox(self.master, textvariable=self.comb_str_1, width=10)
            self.comb_1['value'] = ('RECT', 'CROSS', 'ELLIPSE')
            self.comb_1.current(0)
            self.comb_1.bind('<<ComboboxSelected>>', self.update_erode_morph)
            self.comb_1.place(x=170, y=0)
            # 滚动条触发事件
            self.scl_1 = tk.Scale(self.master, from_=2, resolution=1, to=100, length=100, orient=tk.HORIZONTAL,
                                       label='kernel_w', variable=self.scl_str_1,
                                       command=self.update_erode_morph)
            self.scl_1.set(2)
            self.scl_1.place(x=265, y=0)

            self.scl_2 = tk.Scale(self.master, from_=2, resolution=1, to=100, length=100, orient=tk.HORIZONTAL,
                                        label='kernel_h', variable=self.scl_str_2,
                                        command=self.update_erode_morph)
            self.scl_2.set(2)
            self.scl_2.place(x=370, y=0)

        elif current_text == "Dilate":
            if self.scl_1 is not None:
                self.scl_1.destroy()
            if self.scl_2 is not None:
                self.scl_2.destroy()
            if self.comb_1 is not None:
                self.comb_1.destroy()
            # 当选定算法时，首先初始化一次算法运行结果，当激活了某个参数事件，再次运行和显示算法+参数的结果
            self.image_morph_cv = image_morph.erode_op(self.image_cv)
            self.image_current_cv = np.array(self.image_morph_cv)
            self.image_pil = Image.fromarray(self.image_morph_cv)
            self.show_image()
            # 下拉菜单触发事件
            self.comb_1 = tk.ttk.Combobox(self.master, textvariable=self.comb_str_1, width=10)
            self.comb_1['value'] = ('RECT', 'CROSS', 'ELLIPSE')
            self.comb_1.current(0)
            self.comb_1.bind('<<ComboboxSelected>>', self.update_dilate_morph)
            self.comb_1.place(x=170, y=0)
            # 滚动条触发事件
            self.scl_1 = tk.Scale(self.master, from_=2, resolution=1, to=100, length=100, orient=tk.HORIZONTAL,
                                  label='kernel_w', variable=self.scl_str_1,
                                  command=self.update_dilate_morph)
            self.scl_1.set(2)
            self.scl_1.place(x=265, y=0)

            self.scl_2 = tk.Scale(self.master, from_=2, resolution=1, to=100, length=100, orient=tk.HORIZONTAL,
                                  label='kernel_h', variable=self.scl_str_2,
                                  command=self.update_dilate_morph)
            self.scl_2.set(2)
            self.scl_2.place(x=370, y=0)

        elif current_text == "Open op":
            if self.scl_1 is not None:
                self.scl_1.destroy()
            if self.scl_2 is not None:
                self.scl_2.destroy()
            if self.comb_1 is not None:
                self.comb_1.destroy()
            # 当选定算法时，首先初始化一次算法运行结果，当激活了某个参数事件，再次运行和显示算法+参数的结果
            self.image_morph_cv = image_morph.erode_op(self.image_cv)
            self.image_current_cv = np.array(self.image_morph_cv)
            self.image_pil = Image.fromarray(self.image_morph_cv)
            self.show_image()
            # 下拉菜单触发事件
            self.comb_1 = tk.ttk.Combobox(self.master, textvariable=self.comb_str_1, width=10)
            self.comb_1['value'] = ('RECT', 'CROSS', 'ELLIPSE')
            self.comb_1.current(0)
            self.comb_1.bind('<<ComboboxSelected>>', self.update_open_morph)
            self.comb_1.place(x=170, y=0)
            # 滚动条触发事件
            self.scl_1 = tk.Scale(self.master, from_=2, resolution=1, to=100, length=100, orient=tk.HORIZONTAL,
                                  label='kernel_w', variable=self.scl_str_1,
                                  command=self.update_open_morph)
            self.scl_1.set(2)
            self.scl_1.place(x=265, y=0)

            self.scl_2 = tk.Scale(self.master, from_=2, resolution=1, to=100, length=100, orient=tk.HORIZONTAL,
                                  label='kernel_h', variable=self.scl_str_2,
                                  command=self.update_open_morph)
            self.scl_2.set(2)
            self.scl_2.place(x=370, y=0)
        elif current_text == "Close op":
            if self.scl_1 is not None:
                self.scl_1.destroy()
            if self.scl_2 is not None:
                self.scl_2.destroy()
            if self.comb_1 is not None:
                self.comb_1.destroy()
            # 当选定算法时，首先初始化一次算法运行结果，当激活了某个参数事件，再次运行和显示算法+参数的结果
            self.image_morph_cv = image_morph.erode_op(self.image_cv)
            self.image_current_cv = np.array(self.image_morph_cv)
            self.image_pil = Image.fromarray(self.image_morph_cv)
            self.show_image()
            # 下拉菜单触发事件
            self.comb_1 = tk.ttk.Combobox(self.master, textvariable=self.comb_str_1, width=10)
            self.comb_1['value'] = ('RECT', 'CROSS', 'ELLIPSE')
            self.comb_1.current(0)
            self.comb_1.bind('<<ComboboxSelected>>', self.update_close_morph)
            self.comb_1.place(x=170, y=0)
            # 滚动条触发事件
            self.scl_1 = tk.Scale(self.master, from_=2, resolution=1, to=100, length=100, orient=tk.HORIZONTAL,
                                  label='kernel_w', variable=self.scl_str_1,
                                  command=self.update_close_morph)
            self.scl_1.set(2)
            self.scl_1.place(x=265, y=0)

            self.scl_2 = tk.Scale(self.master, from_=2, resolution=1, to=100, length=100, orient=tk.HORIZONTAL,
                                  label='kernel_h', variable=self.scl_str_2,
                                  command=self.update_close_morph)
            self.scl_2.set(2)
            self.scl_2.place(x=370, y=0)

    def morph_operate(self):
        # 1） 添加下拉菜单，选择形态学滤波算法
        comb = tk.ttk.Combobox(self.master, textvariable=self.var_algo_op)
        comb['value'] = ('--- Select morph algo --- ',
                         '恢复',
                         'Erode',
                         'Dilate',
                         'Open op',
                         'Close op')
        # 这步初始化操作目的：弹出filter窗口的时候，如果不做任何操作，image_filter_cv 是None，
        # 主窗口的image_current都是没有图像的，会报错
        self.image_morph_cv = np.array(self.image_cv)
        comb.current(0)
        comb.bind('<<ComboboxSelected>>', self.get_morph_comb_str)
        comb.place(x=0, rely=0)

    def update_skeleton(self, event):
        bw_th = int(self.scl_str_1.get())
        self.image_skeleton_cv = image_morph.skeleton_extract(self.image_cv, bw_th, self.skeleton_flag)
        self.image_current_cv = np.array(self.image_skeleton_cv)
        self.image_pil = Image.fromarray(self.image_skeleton_cv)
        self.show_image()

    def get_skeleton_comb_str(self, event):
        current_text = self.var_algo_op.get()
        if current_text == "恢复":
            if self.scl_1 is not None:
                self.scl_1.destroy()
            self.image_skeleton_cv = np.array(self.image_cv)
            self.image_current_cv = np.array(self.image_cv)
            self.image_pil = Image.fromarray(self.image_cv)
            self.show_image()
        elif current_text == "skeletonize":
            if self.scl_1 is not None:
                self.scl_1.destroy()
            # 当选定算法时，首先初始化一次算法运行结果，当激活了某个参数事件，再次运行和显示算法+参数的结果
            self.image_skeleton_cv = image_morph.skeleton_extract(self.image_cv, 128, 1)
            self.image_current_cv = np.array(self.image_skeleton_cv)
            self.image_pil = Image.fromarray(self.image_skeleton_cv)
            self.show_image()
            # 滚动条触发事件: 二值化阈值
            self.skeleton_flag = 1
            self.scl_1 = tk.Scale(self.master, from_=1, resolution=1, to=255, length=254, orient=tk.HORIZONTAL,
                                       label='threshold', variable=self.scl_str_1,
                                       command=self.update_skeleton)
            self.scl_1.set(128)
            self.scl_1.place(x=170, y=0)
        elif current_text == "medial_axis":
            if self.scl_1 is not None:
                self.scl_1.destroy()
            # 当选定算法时，首先初始化一次算法运行结果，当激活了某个参数事件，再次运行和显示算法+参数的结果
            self.image_skeleton_cv = image_morph.skeleton_extract(self.image_cv, 128, 2)
            self.image_current_cv = np.array(self.image_skeleton_cv)
            self.image_pil = Image.fromarray(self.image_skeleton_cv)
            self.show_image()
            # 滚动条触发事件: 二值化阈值
            self.skeleton_flag = 2
            self.scl_1 = tk.Scale(self.master, from_=1, resolution=1, to=255, length=254, orient=tk.HORIZONTAL,
                                  label='threshold', variable=self.scl_str_1,
                                  command=self.update_skeleton)
            self.scl_1.set(128)
            self.scl_1.place(x=170, y=0)

    def skeleton_operate(self):
        # 1） 添加下拉菜单，选择形态学滤波算法
        comb = tk.ttk.Combobox(self.master, textvariable=self.var_algo_op)
        comb['value'] = ('--- Select skeleton algo --- ',
                         '恢复',
                         'skeletonize',
                         'medial_axis')
        # 这步初始化操作目的：弹出filter窗口的时候，如果不做任何操作，image_filter_cv 是None，
        # 主窗口的image_current都是没有图像的，会报错
        self.image_skeleton_cv = np.array(self.image_cv)
        comb.current(0)
        comb.bind('<<ComboboxSelected>>', self.get_skeleton_comb_str)
        comb.place(x=0, rely=0)

    def update_resize_method_str(self, event):
        interpolation_str = self.var_algo_op.get()
        if interpolation_str == '双线性差值':
            self.interpolation_method = cv2.INTER_LINEAR
        elif interpolation_str == '最近邻差值':
            self.interpolation_method = cv2.INTER_NEAREST
        elif interpolation_str == '立方差值':
            self.interpolation_method = cv2.INTER_CUBIC
        elif interpolation_str == '区域差值':
            self.interpolation_method = cv2.INTER_AREA
        elif interpolation_str == '兰索斯差值':
            self.interpolation_method = cv2.INTER_LANCZOS4
        else:
            self.interpolation_method = cv2.INTER_LINEAR

    def update_resize(self):
        ratio_x = float(self.scl_str_1.get())
        ratio_y = float(self.scl_str_2.get())
        if ratio_x <= 0 or ratio_x > 1 or ratio_y <= 0 or ratio_y > 1:
            messagebox.showinfo(title="报错", message="压缩系数请输入0-1之间的小数")
        else:
            self.image_resize_cv = cv2.resize(self.image_cv, (0, 0), fx=ratio_x, fy=ratio_y, interpolation=self.interpolation_method)
            self.image_current_cv = np.array(self.image_resize_cv)
            self.image_pil = Image.fromarray(self.image_resize_cv)
            # 新增：如果通过旋转操作更改了画布尺寸，那么必须重新定义画布宽高
            self.width, self.height = self.image_pil.size
            self.imscale = 1.0  # scale for the canvaas image
            self.container = self.canvas.create_rectangle(0, 0, self.width, self.height, width=0)
            self.show_image()

    def update_reset(self):
        self.image_resize_cv = np.array(self.image_cv)
        self.image_current_cv = np.array(self.image_cv)
        self.image_pil = Image.fromarray(self.image_cv)
        # 新增：如果通过旋转操作更改了画布尺寸，那么必须重新定义画布宽高
        self.width, self.height = self.image_pil.size
        self.imscale = 1.0  # scale for the canvaas image
        self.container = self.canvas.create_rectangle(0, 0, self.width, self.height, width=0)
        self.show_image()

    def resize_operate(self):
        tk.Label(self.master, text="水平压缩系数").place(x=0, y=2)
        self.entry_1 = tk.Entry(self.master, textvariable=self.scl_str_1, font=('Arial', 14), width=10)
        self.entry_1.delete(0, "end")
        self.entry_1.insert(0, "0.5")
        self.entry_1.place(x=80, y=0)
        tk.Label(self.master, text="垂直压缩系数").place(x=0, y=32)
        self.entry_2 = tk.Entry(self.master, textvariable=self.scl_str_2, font=('Arial', 14), width=10)
        self.entry_2.delete(0, "end")
        self.entry_2.insert(0, "0.5")
        self.entry_2.place(x=80, y=30)

        # 这步初始化操作目的：弹出filter窗口的时候，如果不做任何操作，image_filter_cv 是None，
        # 主窗口的image_current都是没有图像的，会报错
        self.image_resize_cv = np.array(self.image_cv)
        # 运行button1: 确定
        self.button_1 = tk.Button(self.master, text="确定", command=self.update_resize, width=10)
        self.button_1.place(x=0, y=60)
        # 运行button2：复位
        self.button_2 = tk.Button(self.master, text="恢复", command=self.update_reset, width=10)
        self.button_2.place(x=80, y=60)

        # 1） 添加下拉菜单，选择形态学滤波算法
        comb = tk.ttk.Combobox(self.master, textvariable=self.var_algo_op)
        comb['value'] = ('双线性差值',
                         '最近邻差值',
                         '立方差值',
                         '区域差值',
                         '兰索斯差值')
        comb.current(0)
        comb.bind('<<ComboboxSelected>>', self.update_resize_method_str)
        comb.place(x=200, rely=0)

    def update_neg_rotate(self):
        if self.rotate_flag == 3:
            self.rotate_flag = 0
            self.image_rotate_cv = image_transf.image_rotate(self.image_cv, self.rotate_flag)
        else:
            self.rotate_flag = self.rotate_flag + 1
            self.image_rotate_cv = image_transf.image_rotate(self.image_cv, self.rotate_flag)

        self.image_current_cv = np.array(self.image_rotate_cv)
        self.image_pil = Image.fromarray(self.image_rotate_cv)

        # 新增：如果通过旋转操作更改了画布尺寸，那么必须重新定义画布宽高
        self.width, self.height = self.image_pil.size
        self.imscale = 1.0  # scale for the canvaas image
        self.container = self.canvas.create_rectangle(0, 0, self.width, self.height, width=0)
        print(" 逆时针按钮", self.rotate_flag)
        self.show_image()

    def update_pos_rotate(self):   # flag=0: 0度， flag=-1: 90度， flag=-2: 180度， flag=-3：270度
        if self.rotate_flag == -3:
            self.rotate_flag = 0
            self.image_rotate_cv = image_transf.image_rotate(self.image_cv, self.rotate_flag)
        else:
            self.rotate_flag = self.rotate_flag - 1
            self.image_rotate_cv = image_transf.image_rotate(self.image_cv, self.rotate_flag)

        self.image_current_cv = np.array(self.image_rotate_cv)
        self.image_pil = Image.fromarray(self.image_rotate_cv)

        # 新增：如果通过旋转操作更改了画布尺寸，那么必须重新定义画布宽高
        self.width, self.height = self.image_pil.size
        self.imscale = 1.0  # scale for the canvaas image
        self.container = self.canvas.create_rectangle(0, 0, self.width, self.height, width=0)
        print("顺时针按钮", self.rotate_flag)
        self.show_image()

    def rotate_operate(self):
        # 这步初始化操作目的：弹出binary窗口的时候，如果不做任何操作，image_binary_cv 是None，
        # 主窗口的image_current都是没有图像的，会报错
        self.image_rotate_cv = np.array(self.image_cv)
        # 运行button1
        self.button_1 = tk.Button(self.master, text="顺时针旋转90", command=self.update_pos_rotate)
        self.button_1.place(x=0, y=0)

        # 运行button2
        self.button_2 = tk.Button(self.master, text="逆时针旋转90", command=self.update_neg_rotate)
        self.button_2.place(x=90, y=0)

    def update_hori_flip(self):
        if self.flip_flag == 0:
            self.image_flip_cv = image_transf.image_flip(self.image_cv, 1)
            self.flip_flag = 1
        else:
            self.image_flip_cv = image_transf.image_flip(self.image_current_cv, 1)
        self.image_current_cv = np.array(self.image_flip_cv)
        self.image_pil = Image.fromarray(self.image_flip_cv)
        self.show_image()

    def update_vert_flip(self):
        if self.flip_flag >= 0:
            self.image_flip_cv = image_transf.image_flip(self.image_cv, 0)
            self.flip_flag = 0
            self.flip_flag -= 1
        else:
            self.image_flip_cv = image_transf.image_flip(self.image_current_cv, 0)
        self.image_current_cv = np.array(self.image_flip_cv)
        self.image_pil = Image.fromarray(self.image_flip_cv)
        self.show_image()

    def flip_operate(self):
        # 这步初始化操作目的：弹出binary窗口的时候，如果不做任何操作，image_binary_cv 是None，
        # 主窗口的image_current都是没有图像的，会报错
        self.image_flip_cv = np.array(self.image_cv)

        # 运行button1
        self.button_1 = tk.Button(self.master, text="水平翻转", command=self.update_hori_flip)
        self.button_1.place(x=0, y=0)

        # 运行button2
        self.button_2 = tk.Button(self.master, text="垂直翻转", command=self.update_vert_flip)
        self.button_2.place(x=60, y=0)

    def blob_operate(self):
        pass


    def show_image(self, event=None):

        ''' Show image on the Canvas '''
        bbox1 = self.canvas.bbox(self.container)  # get image area (加载图像的rect坐标)
        # Remove 1 pixel shift at the sides of the bbox1
        bbox1 = (bbox1[0] + 1, bbox1[1] + 1, bbox1[2] - 1, bbox1[3] - 1)
        bbox2 = (self.canvas.canvasx(0),  # get visible area of the canvas （窗口的rect坐标）
                 self.canvas.canvasy(0),
                 self.canvas.canvasx(self.canvas.winfo_width()),
                 self.canvas.canvasy(self.canvas.winfo_height()))
        bbox = [min(bbox1[0], bbox2[0]), min(bbox1[1], bbox2[1]),  # get scroll region box
                max(bbox1[2], bbox2[2]), max(bbox1[3], bbox2[3])]
        if bbox[0] == bbox2[0] and bbox[2] == bbox2[2]:  # whole image in the visible area
            bbox[0] = bbox1[0]
            bbox[2] = bbox1[2]
        if bbox[1] == bbox2[1] and bbox[3] == bbox2[3]:  # whole image in the visible area
            bbox[1] = bbox1[1]
            bbox[3] = bbox1[3]
        self.canvas.configure(scrollregion=bbox)  # set scroll region
        x1 = max(bbox2[0] - bbox1[0], 0)  # get coordinates (x1,y1,x2,y2) of the image tile
        y1 = max(bbox2[1] - bbox1[1], 0)
        x2 = min(bbox2[2], bbox1[2]) - bbox1[0]
        y2 = min(bbox2[3], bbox1[3]) - bbox1[1]
        if int(x2 - x1) > 0 and int(y2 - y1) > 0:  # show image if it in the visible area
            x = min(int(x2 / self.imscale), self.width)   # sometimes it is larger on 1 pixel...
            y = min(int(y2 / self.imscale), self.height)  # ...and sometimes not
            image = self.image_pil.crop((int(x1 / self.imscale), int(y1 / self.imscale), x, y))
            imagetk = ImageTk.PhotoImage(image.resize((int(x2 - x1), int(y2 - y1))))
            imageid = self.canvas.create_image(max(bbox2[0], bbox1[0]), max(bbox2[1], bbox1[1]), anchor='nw', image=imagetk)

            # TODO: 以下代码用来获取图像坐标，但是还是有些问题
            # def callback(e):
            #     x, y = canvas_to_image_cords( x=e.x, y=e.y, tagOrId='img')  # Can also pass img_tag as tagOrId
            #     print(x, y)
            # def canvas_to_image_cords(x: int, y: int, tagOrId=''):
            #     anchor = 'nw'
            #     w, h = self.canvas.winfo_reqwidth(), self.canvas.winfo_reqheight()
            #     if anchor == 'center':
            #         img_xpos, img_ypos = bbox1[2] - 1, bbox1[3] - 1
            #         # img_xpos, img_ypos = image.width() / 1, image.height() / 1
            #         start_x, start_y = img_xpos - w / 1, img_ypos - h / 1
            #     elif anchor == 'nw':
            #         start_x, start_y = 0, 0
            #     req_x, req_y = start_x + x, start_y + y
            #     return req_x, req_y
            # self.canvas.bind('<1>', callback)


            self.canvas.lower(imageid)     # set image into background
            self.canvas.imagetk = imagetk  # keep an extra reference to prevent garbage-collection

    def close_win(self):
        # self.master.after_cancel(self.show_image())
        if messagebox.askokcancel("Message", "是否要关闭当前窗口?"):
            self.master.destroy()
            self.is_close_win = True


if __name__ == "__main__":
    path = r'C:\Users\admin\Desktop\dt\image.bmp'  # place path to your image here
    image_cv = cv2.imread(path, -1)
    root = Tk()
    # root = tk.Toplevel()
    app = Zoom_Advanced(root, image_cv, win_title_str="test")
    app.show_image()
    binary_image = app.resize_operate()
    root.mainloop()



